package security.security.login;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.util.StringUtils;
import security.security.Utils;

public class CustomAuthenticationProvider implements AuthenticationProvider {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private UserDetailsService userDetailsService;
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    public CustomAuthenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    public Authentication authenticate(Authentication authenticate) throws AuthenticationException {

        String username = authenticate.getName();
        String password = String.valueOf(authenticate.getCredentials());
        String from = Utils.getRequest().getParameter("from");

        logger.debug("from: {}", from);
        logger.debug("username: {}", username);
        logger.debug("credential: {}", password);

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = null;
        if("1".equals(from)){
            usernamePasswordAuthenticationToken = loginByUsernameAndPassword(username, password);
        }else if("2".equals(from)){
            usernamePasswordAuthenticationToken = loginByWxOpenid(username);
        }
        return usernamePasswordAuthenticationToken;
    }

    public UsernamePasswordAuthenticationToken loginByUsernameAndPassword(String username, String password) throws AuthenticationException {
        logger.debug("login from: loginByUsernameAndPassword");

        UserDetails userDetails = null;

        if(!StringUtils.isEmpty(username)) {
            userDetails = userDetailsService.loadUserByUsername(username);
        }
        checkAccount(userDetails);
        // match password
        if(!bCryptPasswordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("密码错误");
        }
        // 验证身份
        return new UsernamePasswordAuthenticationToken(userDetails, password, userDetails.getAuthorities());
    }

    public UsernamePasswordAuthenticationToken loginByWxOpenid(String wxOpenid) throws AuthenticationException {
        logger.debug("login from: loginByWxOpenid");

        UserDetails userDetails = null;

        if(!StringUtils.isEmpty(wxOpenid)) {
            userDetails = ((CustomUserDetailsService)userDetailsService).loadUserByWxOpenid(wxOpenid);
        }
        checkAccount(userDetails);
        // 验证身份
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }

    private void checkAccount(UserDetails userDetails){
        logger.debug("userDetails {}", userDetails);

        if(userDetails == null) {
            logger.error("账号不存在");
            throw new UsernameNotFoundException("账号不存在.");
        }else if (!userDetails.isEnabled()){
            logger.error("账号已被禁用");
            throw new DisabledException("账号已被禁用");
        }else if (!userDetails.isAccountNonExpired()) {
            logger.error("账号已过期");
            throw new AccountExpiredException("账号已过期");
        }else if (!userDetails.isAccountNonLocked()) {
            logger.error("账号已被锁定");
            throw new LockedException("账号已被锁定");
        }else if (!userDetails.isCredentialsNonExpired()) {
            logger.error("凭证已过期");
            throw new CredentialsExpiredException("凭证已过期");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        //返回true后才会执行上面的authenticate方法,这步能确保authentication能正确转换类型
        logger.debug("supports: {}", UsernamePasswordAuthenticationToken.class.equals(authentication));
        return UsernamePasswordAuthenticationToken.class.equals(authentication);
    }

//    public
}
